<template>
  <div class="editor-panel">
    <splitpanes horizontal class="default-theme">
      <pane size="40" max-size="70" min-size="10">
        <code-editor @query="loadData" />
      </pane>
      <pane>
        <div class="query-result" :class="{ 'is-fullscreen': result.isFullscreen }">
          <el-link size="small" @click="onToggleFullscreen" class="btn-fullscreen">
            <el-icon>
              <full-screen />
            </el-icon>
          </el-link>
          <el-tabs type="border-card" v-model="result.activeTab">
            <el-tab-pane label="执行结果" name="result-summary">
              <div v-if="result.queried" class="query-result--info">
                <ul>
                  <li>
                    <div v-if="result.success" style="color: darkcyan">
                      <span>执行成功</span>
                      <span class="result-time" style="margin-left: 20px; color: #666666">{{ result.time }}</span>
                    </div>
                    <div v-else>
                      <span class="query-result--message" :class="{ standout: result.standout }">
                        <span class="msg-title">执行失败</span>
                        <span class="msg-content">{{ result.message }}</span>
                      </span>
                    </div>
                  </li>
                  <li v-for="(data, i) in result.dataset" :key="i">
                    <span>结果{{ i + 1 }}: </span>
                    <span>影响 {{ data.rowsAffected }} 行</span>
                  </li>
                </ul>
              </div>
            </el-tab-pane>
            <el-tab-pane v-for="(data, tabIndex) in result.dataset" :key="tabIndex" :label="`结果${tabIndex + 1}`"
                         :name="`data-${tabIndex}`">
              <div class="data-table" v-if="data.query">
                <el-table size="small" :data="data.data" height="100%" header-row-class-name="data-header"
                          @row-dblclick="(row) => onRowDblClick(tabIndex, row, data)" highlight-current-row stripe
                          border fit>
                  <el-table-column v-for="(col) in data.columns" :label="col" :key="col" :prop="col"
                                   show-overflow-tooltip>
                    <template #default="{ row }">
                      <span class="null-cell" v-if="row[col] === null">NULL</span>
                      <span class="value-cell" v-else>{{ row[col] }}</span>
                    </template>
                  </el-table-column>
                </el-table>
                <el-pagination :total="data.rowsAffected" v-model:page-size="queryOption.pageSize"
                               v-model:current-page="queryOption.pageIndex" :page-sizes="result.sizeList"
                               layout="slot,total,sizes,prev,pager,next,jumper" size="small">
                  <template #default>
                    <div class="data-table-footer">
                      <el-button size="small" @click="onAddRow(tabIndex, data)" title="添加记录" class="btn-add-row">
                        <el-icon>
                          <plus></plus>
                        </el-icon>
                      </el-button>
                      <span class="data-range">
                        <span>第</span>
                        <span>{{ offset + 1 }}</span>
                        <span>-</span>
                        <span>{{ offset + data.data.length }}</span>
                        <span>条</span>
                      </span>
                    </div>
                  </template>
                </el-pagination>
              </div>
              <div style="font-size: small; padding: 20px" v-else>
                <span>影响 {{ data.rowsAffected }} 行</span>
              </div>
            </el-tab-pane>
          </el-tabs>
        </div>
      </pane>
    </splitpanes>
    <el-dialog :title="`列: ${cellValue.title}`" v-model="cellValue.visible" width="480px">
      <textarea class="cell-value-preview" :value="cellValue.content"></textarea>
    </el-dialog>
    <el-drawer :title="editDialogTitle" v-model="rowValue.visible" size="100%">
      <row-editor :data="rowValue.data" :columns="rowValue.columns"
                  :databases="rowValue.databases" :editable="editable"
                  :tables="rowValue.tables" @close="onEditorClose" v-if="rowValue.visible" />
    </el-drawer>
  </div>
</template>

<script setup>
import CodeEditor from '@/components/editor/CodeEditor.vue'
import { FullScreen, Plus } from '@element-plus/icons-vue'
import RowEditor from '@/components/RowEditor.vue'
</script>
<script>
import { ElMessage } from 'element-plus'
import apis from '@/assets/apis'

function setRowIndex(row, index) {
  Object.defineProperty(row, '$__index__$', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: index
  })
}

export default {
  name: 'EditorPanel',
  props: {
    statement: String,
    connection: String,
    database: String
  },
  provide() {
    return {
      context: this
    }
  },
  data() {
    return {
      environ: {
        connection: null,
        database: null,
        statement: ''
      },
      queryOption: {
        pageIndex: 1,
        pageSize: 100
      },
      result: {
        activeTab: '0',
        standout: false,
        isFullscreen: false,
        sizeList: [10, 20, 50, 100, 200, 500, 1000],
        success: false,
        message: null,
        dataset: [],
        queried: false,
        time: 0
      },
      cellValue: {
        visible: false,
        title: '',
        content: ''
      },
      rowValue: {
        tabIndex: 0,
        visible: false,
        data: [],
        tables: [],
        databases: [],
        columns: []
      }
    }
  },
  created() {
    this.environ.connection = this.connection
    this.environ.database = this.database || 'mysql'
    this.environ.statement = this.statement
    this.loadData()
  },
  watch: {
    statement() {
      this.environ.statement = this.statement
    },
    queryOption: {
      deep: true,
      handler() {
        this.loadData()
      }
    }
  },
  computed: {
    offset() {
      return (this.queryOption.pageIndex - 1) * this.queryOption.pageSize
    },
    editable() {
      // 条件：只有一个数据库，只有一个表
      const { databases, tables } = this.rowValue

      const dbs = new Set(databases)
      if (dbs.length > 1) {
        return false
      }
      const tabs = new Set(tables)
      if (tabs.length > 1) {
        return false
      }
      return true
    },
    editDialogTitle() {
      if (!this.rowValue.visible) {
        return ''
      }
      if (this.editable) {
        return `行数据 - ${this.rowValue.databases[0]}.${this.rowValue.tables[0]}`
      } else {
        return '行数据'
      }
    }
  },
  methods: {
    onAddRow(tabIndex, { columns, tables, databases }) {
      this.rowValue.tabIndex = tabIndex
      this.rowValue.data = null
      this.rowValue.tables = tables
      this.rowValue.databases = databases
      this.rowValue.columns = columns
      this.rowValue.visible = true
    },
    onEditorClose(e) {
      this.rowValue.visible = false
      // 在添加数据时，data 为 null
      const rowIndex = this.rowValue.data ? this.rowValue.data.$__index__$ : null
      this.rowValue.data = null
      if (!e) {
        return
      }
      // action: add edit delete
      const action = e.action
      if (!action) {
        // 没有操作，直接关闭了
        return
      }

      // const key = e.key
      // const id = e.id
      const data = e.data
      const activeTabIndex = this.rowValue.tabIndex
      // columns data
      const dataset = this.result.dataset[activeTabIndex]
      if (action === 'delete') {
        // 删除表格对应行数据
        dataset.data.splice(rowIndex, 1)
      } else if (action === 'edit') {
        // 更新表格对应行数据
        setRowIndex(data, rowIndex)
        dataset.data[rowIndex] = data
      } else {
        // 添加数据
        const index = dataset.data.length
        setRowIndex(data, index)
        dataset.data.push(data)
      }
    },
    onCellDblClick(row, column) {
      this.cellValue.title = column.property
      this.cellValue.content = row[column.getColumnIndex()]
      this.cellValue.visible = true
    },
    onRowDblClick(tabIndex, row, { columns, tables, databases }) {
      this.rowValue.tabIndex = tabIndex
      this.rowValue.data = row
      this.rowValue.tables = tables
      this.rowValue.databases = databases
      this.rowValue.columns = columns
      this.rowValue.visible = true
    },
    onToggleFullscreen() {
      this.result.isFullscreen = !this.result.isFullscreen
    },
    formatTimeSpan(span) {
      if (span < 1000) {
        return `${span}ms`
      }

      return `${span / 1000}s`
    },
    loadData(sql) {
      if (!arguments.length) {
        sql = this.environ.statement
      }
      if (!this.environ.connection) {
        return
      }
      if (!this.environ.database) {
        return
      }
      if (!sql) {
        return
      }
      this.result.dataset = []
      const startTime = new Date()
      const connection = this.$store.state.connections.filter(item => item.label === this.environ.connection)[0]
      this.$store.commit('updateGlobalMsg', '正在加载数据...')
      apis.dbm.connection.execute(connection, this.environ.database, {
        fetchOffset: (this.queryOption.pageIndex - 1) * this.queryOption.pageSize,
        fetchSize: this.queryOption.pageSize,
        statement: sql
      }).then(dataset => {
        const time = new Date() - startTime
        this.result.time = this.formatTimeSpan(time)
        this.result.success = true
        this.result.message = null
        // // 处理列结构，兼容 table-v2
        // data.forEach(table => {
        //   table.data = table.data.map((row) => {
        //     const cells = {}
        //     table.columns.forEach((col, i) => {
        //       cells[col] = row[i]
        //     })
        //     return cells
        //   })
        //   table.columns = table.columns.map((col) => {
        //     return {
        //       title: col,
        //       key: col,
        //       dataKey: col,
        //       minWidth: 100,
        //       class: 'auto-ellipsis'
        //     }
        //   })
        // })

        // 将行数据结构从数组处理成对象
        this.result.dataset = dataset.map(dataTable => {
          dataTable.data = dataTable.data.map((cells, rowIndex) => {
            const row = {}
            setRowIndex(row, rowIndex)
            dataTable.columns.forEach((column, index) => {
              row[column] = cells[index]
            })
            return row
          })

          return dataTable
        })
        this.$store.commit('updateGlobalMsg', '加载完成')
        this.result.activeTab = dataset.length ? 'data-0' : 'result-summary'
      }).catch(e => {
        this.result.success = false
        this.result.message = e.message
        this.result.activeTab = 'result-summary'
        this.$store.commit('updateGlobalMsg', `错误： ${e.message}`)
        ElMessage.error(e.message)
      }).finally(() => {
        this.result.queried = true
      })
    }
  }
}
</script>

<style lang="less" scoped>
.editor-panel {
  height: 100%;
}

.query-result {
  height: 100%;
  position: relative;

  &.is-fullscreen {
    position: fixed;
    z-index: 102;
    left: 0;
    top: 40px;
    width: 100%;
    height: 100%;
  }
}

.el-tab-pane {
  height: 100%;
}

.query-result--info {
  height: 100%;
  overflow: auto;
}

ul {
  list-style: none;
}

li {
  font-size: 13px;
  padding: 5px 0;
}

.query-result--message {
  display: inline-block;
  margin: 5px;
  padding: 10px 10px 15px 10px;
  border-left: 4px solid #f8a2a2;
  background-color: #fff5f5;
  font-family: Consolas, Arial, serif;
  font-size: 13px;
  border-radius: 2px;
  color: #000;
  white-space: break-spaces;

  &.standout {
    .msg-content {
      color: red;
      transition: color 1s;
    }
  }
}

.msg-title {
  display: block;
  color: red;
  padding: 0 0 10px 0;
  font-size: 16px;
  font-family: "Microsoft JhengHei";
}

.btn-fullscreen {
  position: absolute;
  right: 15px;
  top: 12px;
  z-index: 1;
}

.query-statement {
  display: block;
  background-color: #f7f7f7;
  margin-top: 5px;
  margin-right: 10px;
  padding: 5px 10px;
  border-radius: 4px;
  border: 1px solid #f0f0f0;
  white-space: break-spaces;
}

.data-table {
  height: 100%;
  position: relative;

  // .el-auto-resizer {
  .el-table {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    bottom: 40px;
    height: auto !important;
  }

  .el-pagination {
    position: absolute;
    height: 32px;
    left: 5px;
    right: 0;
    bottom: 5px;
    text-align: right;
  }

  .data-range {
    margin-right: 10px;
    font-weight: normal;
    color: #666666;

    > span {
      min-width: auto;
      padding: 0 2px;
    }
  }
}

.cell-value-preview {
  width: 100%;
  height: 200px;
  overflow: auto;
  font-size: 14px;
  font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;
  padding: 5px;
  word-break: break-word;
  border: 1px solid #dddddd;
  border-radius: 4px;
}

.data-table-footer {
  display: flex;
  align-items: center;
  gap: 40px;
}

.btn-add-row {
  color: #0363d1;

  :deep(.el-icon) {
    font-size: 1.5em;
  }
}
</style>
<style lang="less">
.data-header .cell {
  font-weight: bold;
  color: #000;
}
</style>
